package com.sbtr.util;


import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * wav音频文件工具类
 *
 * @author ex_chenyj
 *
 */
public class WavFileUtil {

	private static Logger logger = LoggerFactory.getLogger(WavFileUtil.class);

	public static void main(String[] args) throws IOException {

		// wav文件信息获取
		String dir = "D://0-1.wav";
		File f = new File(dir);
		String absolutePath = f.getAbsolutePath();
		System.out.println(absolutePath);
		RandomAccessFile rdf = null;
		rdf = new RandomAccessFile(f, "r");

		System.out.println("audio size: " + toInt(read(rdf, 4, 4))); // 音频文件大小

		System.out.println("audio format: " + toShort(read(rdf, 20, 2))); // 音频格式，1-PCM

		System.out.println("num channels: " + toShort(read(rdf, 22, 2))); // 1-单声道；2-双声道

		System.out.println("sample rate: " + toInt(read(rdf, 24, 4))); // 采样率、音频采样级别

		System.out.println("byte rate: " + toInt(read(rdf, 28, 4))); // 每秒波形的数据量

		System.out.println("block align: " + toShort(read(rdf, 32, 2))); // 采样帧的大小

		System.out.println("bits per sample: " + toShort(read(rdf, 34, 2))); // 采样位数

		int targetLen = (int) (rdf.length() - 44);
		byte[] data = read(rdf, 44, targetLen);
		double pow = Math.pow(2, 15);
		for (int i = 0; i < 100; i += 4) {
			byte[] b11 = new byte[] { data[i], data[i + 1] };
			System.out.println(toShort(b11) / pow);

		}
		rdf.close();
	}

	public static int toInt(byte[] b) {
		return (((b[3] & 0xff) << 24) + ((b[2] & 0xff) << 16) + ((b[1] & 0xff) << 8) + ((b[0] & 0xff) << 0));
	}

	public static short toShort(byte[] b) {
		return (short) (((b[1] & 0xff) << 8) + ((b[0] & 0xff) << 0));
	}

	public static byte[] read(RandomAccessFile rdf, int pos, int length) throws IOException {
		rdf.seek(pos);
		byte[] result = new byte[length];
		rdf.read(result);
		return result;
	}

	/**
	 * 将MultipartFile 转File(临时文件)
	 *
	 * @param multiFile
	 * @return
	 */
	public static File multipartFileToFile(MultipartFile multiFile) {
		// 获取文件名
		String fileName = multiFile.getOriginalFilename();
		// 获取文件后缀
		String prefix = fileName.substring(fileName.lastIndexOf("."));
		// 用当前时间作为文件名，防止生成的临时文件重复
		try {
			File file = File.createTempFile(System.currentTimeMillis() + "", prefix);
			multiFile.transferTo(file);
			return file;
		} catch (Exception e) {
			logger.error("临时文件生成异常", e);
		}
		return null;
	}

	/**
	 * 根据文件名判断此文件是否为wav音频文件
	 *
	 * @param fileName
	 * @return
	 */
	public static boolean isWav(String fileName) {
		if (StringUtils.isNotEmpty(fileName)) {
			Set<String> imageSuffix = new HashSet<>(Arrays.asList(".wav"));
			for (String suffix : imageSuffix) {
				if (fileName.endsWith(suffix)) {
					return true;
				}
			}
		}
		return false;
	}

	public static WavInfo getWavInfo(MultipartFile multiFile) throws IOException {
		WavInfo info = new WavInfo();
		long start1 = System.currentTimeMillis();
		File file = multipartFileToFile(multiFile);
		long end1 = System.currentTimeMillis();
		logger.info("1.MultipartFile转File耗时: {}豪秒",(end1 - start1));
		// 获取时长
		long second = 0;
		if (null != file) {
			if (file.exists()) {
				try {
					it.sauronsoftware.jave.Encoder encoder = new it.sauronsoftware.jave.Encoder();
					it.sauronsoftware.jave.MultimediaInfo m = encoder.getInfo(file);
					long ls = m.getDuration();
					Assert.isTrue(ls != 0, "音频的时长为0");
					second = ls / 1000;
					if (second == 0) {
						second = 1;
					}
					logger.warn("此音频时长为:{}秒！",second);
				} catch (Exception e) {
					logger.error("获取音频时长失败, 音频dir:{}", file.getAbsolutePath(), e);
				}
			} else {
				logger.error("音频不存在, 音频dir:{}", file.getAbsolutePath());
			}
			Assert.isTrue(second <= 10, "请使用时长小于等于10秒的音频");
			RandomAccessFile rdf = null;
			rdf = new RandomAccessFile(file, "r");
			// 获取音频信息
			// 声道数
			short numChannels = toShort(read(rdf, 22, 2));
			// 采样率
			int sampleRate = toInt(read(rdf, 24, 4));
			// 采样位数
			short bitsPerSample = toShort(read(rdf, 34, 2));
			info.setNumChannels(numChannels);
			info.setSampleRate(sampleRate);
			info.setBitsPerSample(bitsPerSample);
			info.setFileName(multiFile.getOriginalFilename());

			// 声音文件的实体内容(即去除头部信息后剩下的,从字节数据的44索引开始)
			int targetLen = (int) (rdf.length() - 44);
			info.setTargetLen(targetLen);

			long start2 = System.currentTimeMillis();
			byte[] data = read(rdf, 44, targetLen);
			long end2 = System.currentTimeMillis();
			logger.info("2.读取字节数组耗时: {}豪秒",(end2 - start2));
			long start3 = System.currentTimeMillis();
			String wavDataBase64 = getWavDataBase64(info, data);
			long end3 = System.currentTimeMillis();
			logger.info("3.字节数组转换base64总耗时: {}豪秒",(end3 - start3));
			info.setDataBase64Str(wavDataBase64);
			// 删除掉临时文件
			if (file.exists()) {
				file.delete();
			}
			return info;
		}
		return info;
	}

	/**
	 * 音频文件主体内容转换base64
	 *
	 * @param info
	 * @param data
	 * @return
	 */
	private static String getWavDataBase64(WavInfo info, byte[] data) {
		Short numChannels = info.getNumChannels();
		Short bitsPerSample = info.getBitsPerSample();
		Integer targetLen = info.getTargetLen();
		double pow = Math.pow(2, 15);

		StringBuilder sb = new StringBuilder();
		// 判断是单声道还是双声道
		if (1 == numChannels) {
			// 判断采样位数
			if (8 == bitsPerSample) {

			} else if (16 == bitsPerSample) {
				// 单声道16位,则每两个字节转换一个short类型
				// 即data[0]data[1],data[2]data[3],...
				for (int i = 0; i < targetLen; i += 2) {
					byte[] b = new byte[] { data[i], data[i + 1] };
					sb.append(toShort(b) / pow);
					sb.append(System.lineSeparator());
				}
			} else if (32 == bitsPerSample) {
				// 单声道32位,则每4个字节转换一个int类型
				// 即data[0]data[1]data[2]data[3],data[4]data[5]data[6]data[7],...
				for (int i = 0; i < targetLen; i += 4) {
					byte[] b = new byte[] { data[i], data[i + 1], data[i + 2], data[i + 3] };
					sb.append(toInt(b) / pow);
					sb.append(System.lineSeparator());
				}
			}
		} else if (2 == numChannels) {
			// 判断采样位数
			if (8 == bitsPerSample) {
			} else if (16 == bitsPerSample) {
				// 双声道16位,则每两个字节转换一个short类型,并且隔开获取
				// 即data[0]data[1],data[2]data[3]跳过,data[4]data[5],data[6]data[7]跳过,...
				long start4 = System.currentTimeMillis();
				for (int i = 0; i < targetLen; i += 4) {
					byte[] b = new byte[] { data[i], data[i + 1] };
					sb.append(toShort(b) / pow);
					sb.append(System.lineSeparator());
				}
				long end4 = System.currentTimeMillis();
				logger.info("4.循环读取2字节转short总耗时: {}豪秒", (end4 - start4));
			} else if (32 == bitsPerSample) {
				// 双声道32位,则每4个字节转换一个int类型,并且隔开获取
				// 即data[0]data[1]data[2]data[3],data[4]data[5]data[6]data[7]跳过,data[8]data[9]data[10]data[11],data[12]data[13]data[14]data[15]跳过,...
				for (int i = 0; i < targetLen; i += 8) {
					byte[] b = new byte[] { data[i], data[i + 1], data[i + 2], data[i + 3] };
					sb.append(toInt(b) / pow);
					sb.append(System.lineSeparator());
				}
			}
		}
		String str = sb.toString();
		// 采用百度的base64工具类,效率更高
		long start7 = System.currentTimeMillis();
		String encode = Base64Util.encode(str);
		long end7 = System.currentTimeMillis();
		logger.info("5.百度工具类base64转换处理耗时:{} 豪秒", (end7 - start7));
		return encode;
	}

	@Getter
	@Setter
	public static class WavInfo {
		private String fileName;
		private Short numChannels; // 声道数
		private Integer sampleRate; // 采样率
		private Short bitsPerSample; // 采样位数
		private String dataBase64Str; // 除去头部信息后,音频数据转换后base64字符串
		private Integer targetLen; // wav文件的主体字节长度
	}

}
